
subroutine grmult(rcoslat ,d       ,qm1     ,tm1     ,um1     ,&
                  vm1     ,z       ,tm2     ,phis    ,dpsl    ,&
                  dpsm    ,omga    ,pdel    ,pbot    ,logpsm2 ,&
                  logpsm1 ,rpmid   ,rpdel   ,fu      ,fv      ,&
                  t2      ,ut      ,vt      ,drhs    ,pmid    ,&
                  etadot  ,etamid  ,engy    ,ddpn    ,vpdsn   ,&
                  dpslon  ,dpslat  ,vat     ,ktoop   ,nlon    )

!----------------------------------------------------------------------- 
! 
! Purpose: 
! Non-linear dynamics calculations in grid point space
! 
! Method: 
! 
! Author: 
! Original version:  CCM1
! Standardized:      J. Rosinski, June 1992
! Reviewed:          B. Boville, D. Williamson, J. Hack, August 1992
! Reviewed:          B. Boville, D. Williamson, April 1996
!
!-----------------------------------------------------------------------
!
! $Id$
! $Author$
!
!-----------------------------------------------------------------------
   use shr_kind_mod, only: r8 => shr_kind_r8
   use pmgrid,       only: plon, plev, plevp, plon
   use pspect
   use commap
   use physconst, only: rair, cappa, cpvir, zvir
   use hycoef, only : hybi, hybm, hybd, nprlev

   implicit none

!
! Input arguments
!
   real(r8), intent(in) :: rcoslat              ! 1./cosine(latitude)
   real(r8), intent(in) :: d(plon,plev)         ! divergence
   real(r8), intent(in) :: qm1(plon,plev)       ! specific humidity
   real(r8), intent(in) :: tm1(plon,plev)       ! temperature
   real(r8), intent(in) :: um1(plon,plev)       ! zonal wind * cos(lat)
   real(r8), intent(in) :: vm1(plon,plev)       ! meridional wind * cos(lat)
   real(r8), intent(in) :: z(plon,plev)         ! vorticity
   real(r8), intent(in) :: phis(plon)           ! surface geopotential
   real(r8), intent(in) :: dpsl(plon)           ! longitudinal component of grad ln(ps)
   real(r8), intent(in) :: dpsm(plon)           ! latitudinal component of grad ln(ps)
   real(r8), intent(in) :: omga(plon,plev)      ! vertical pressure velocity
   real(r8), intent(in) :: pdel(plon,plev)      ! layer thicknesses (pressure)
   real(r8), intent(in) :: pbot(plon)           ! bottom interface pressure
   real(r8), intent(in) :: logpsm2(plon)        ! log(psm2)
   real(r8), intent(in) :: logpsm1(plon)        ! log(ps)
   real(r8), intent(in) :: rpmid(plon,plev)     ! 1./pmid
   real(r8), intent(in) :: rpdel(plon,plev)     ! 1./pdel
   real(r8), intent(in) :: tm2(plon,plev)       ! temperature at previous time step
   integer, intent(in) :: nlon
!
! Input/Output arguments
!
   real(r8), intent(inout) :: fu(plon,plev)        ! nonlinear term - u momentum eqn
   real(r8), intent(inout) :: fv(plon,plev)        ! nonlinear term - v momentum eqn
   real(r8), intent(inout) :: t2(plon,plev)        ! nonlinear term - temperature
   real(r8), intent(inout) :: ut(plon,plev)        ! (u*TM1) - heat flux - zonal 
   real(r8), intent(inout) :: vt(plon,plev)        ! (u*TM1) - heat flux - meridional
   real(r8), intent(inout) :: drhs(plon,plev)      ! RHS of divergence eqn (del^2 term)
   real(r8), intent(inout) :: pmid(plon,plev)      ! pressure at full levels
   real(r8), intent(inout) :: etadot(plon,plevp)   ! vertical velocity in eta coordinates
   real(r8), intent(in)    :: etamid(plev)         ! midpoint values of eta (a+b)
   real(r8), intent(inout) :: engy(plon,plev)      ! kinetic energy
!
! Output arguments
!
   real(r8), intent(out) :: ddpn(plon)           ! complete sum of d*delta p
   real(r8), intent(out) :: vpdsn(plon)          ! complete sum V dot grad(ln(ps)) delta b
   real(r8), intent(out) :: dpslat(plon,plev)    ! ln(ps) component of lon press gradient
   real(r8), intent(out) :: dpslon(plon,plev)    ! ln(ps) component of lat press gradient
   real(r8), intent(out) :: vat   (plon,plev)    ! Vertical advection of temperature
   real(r8), intent(out) :: ktoop (plon,plev)    ! (Kappa*T)*(omega/P)

!
!---------------------------Local workspace-----------------------------
!
   real(r8) tv(plon,plev)        ! virtual temperature
   real(r8) ddpk(plon)           ! partial sum of d*delta p
   real(r8) vkdp                 ! V dot grad(ln(ps))
   real(r8) vpdsk(plon)          ! partial sum  V dot grad(ln(ps)) delta b
   real(r8) tk0(plon)            ! tm1 at phony level 0
   real(r8) uk0(plon)            ! u at phony level 0
   real(r8) vk0(plon)            ! v at phone level 0
   real(r8) rtv(plon,plev)       ! rair*tv
   real(r8) pterm(plon,plev)     ! intermediate term for hydrostatic eqn
   real(r8) tterm(plon,plev)     ! intermediate term for hydrostatic eqn
   real(r8) tmp                  ! temporary workspace
   real(r8) tmpk                 ! temporary workspace
   real(r8) tmpkp1               ! temporary workspace
   real(r8) edotdpde(plon,plevp) ! etadot*dp/deta
   real(r8) udel(plon,0:plev-1)  ! vertical u difference
   real(r8) vdel(plon,0:plev-1)  ! vertical v difference
   real(r8) tdel(plon,0:plev-1)  ! vertical TM1 difference

   integer i,k,kk             ! longitude, level indices
!
! Initialize arrays which represent vertical sums (ddpk, ddpn, vpdsk,
! vpdsn).  Set upper boundary condition arrays (k=0: tk0, uk0, vk0).
!
   ddpk  = 0.0_r8
   ddpn  = 0.0_r8
   vpdsk = 0.0_r8
   vpdsn = 0.0_r8
   tk0   = 0.0_r8
   uk0   = 0.0_r8
   vk0   = 0.0_r8
!
! Virtual temperature
!
   call virtem(nlon,plon,plev,tm1,qm1,zvir,tv)

!$OMP PARALLEL DO PRIVATE (K, I)
   do k=1,plev
      do i=1,nlon
         rtv(i,k) = rair*tv(i,k)
      end do
   end do
!
!$OMP PARALLEL DO PRIVATE (I, K, VKDP)
   do i=1,nlon
!
! sum(plev)(d(k)*dp(k))
!
      do k=1,plev
         ddpn(i) = ddpn(i) + d(i,k)*pdel(i,k)
      end do
!
! sum(plev)(v(k)*grad(lnps)*db(k))
!
      do k=nprlev,plev
         vkdp = rcoslat*(um1(i,k)*dpsl(i) + vm1(i,k)*dpsm(i))*pbot(i)
         vpdsn(i) = vpdsn(i) + vkdp*hybd(k)
      end do
!
! Compute etadot (dp/deta) (k+1/2).  Note: sum(k)(d(j)*dp(j)) required in
! pressure region. sum(k)(d(j)*dp(j)) and sum(k)(v(j)*grad(ps)*db(j)) 
! required in hybrid region
!
      edotdpde(i,1) = 0._r8
      do k=1,nprlev-1
         ddpk(i) = ddpk(i) + d(i,k)*pdel(i,k)
         edotdpde(i,k+1) = -ddpk(i)
      end do
!
      do k=nprlev,plev-1
         ddpk(i) = ddpk(i) + d(i,k)*pdel(i,k)
         vkdp = rcoslat*(um1(i,k)*dpsl(i) + vm1(i,k)*dpsm(i))*pbot(i)
         vpdsk(i) = vpdsk(i) + vkdp*hybd(k)
         edotdpde(i,k+1) = -ddpk(i) - vpdsk(i) + hybi(k+1)*(ddpn(i)+vpdsn(i))
      end do
      edotdpde(i,plevp) = 0._r8
!
!
   end do

!
! Nonlinear advection terms.  u*tm1, v*tm1, kinetic energy first
!
!$OMP PARALLEL DO PRIVATE (K, I)
   do k=1,plev
      do i=1,nlon
         ut(i,k) = um1(i,k)*tm1(i,k)
         vt(i,k) = vm1(i,k)*tm1(i,k)
         engy(i,k) = 0.5_r8*(um1(i,k)**2 + vm1(i,k)**2)
      end do
   end do
!
! Compute workspace arrays for delta-u, delta-v, delta-tm1 (k)
!
!$OMP PARALLEL DO PRIVATE (K, I)
   do k=0,plev-1
      if (k == 0) then
         do i=1,nlon
            udel(i,0) = um1(i,1) - uk0(i)
            vdel(i,0) = vm1(i,1) - vk0(i)
            tdel(i,0) = tm1(i,1) - tk0(i)
         end do
      else
         do i=1,nlon
            udel(i,k) = um1(i,k+1) - um1(i,k)
            vdel(i,k) = vm1(i,k+1) - vm1(i,k)
            tdel(i,k) = tm1(i,k+1) - tm1(i,k)
         end do
      endif
   end do
!
!$OMP PARALLEL DO PRIVATE (K, I, TMPK, TMPKP1, TMP)
   do k=1,plev
!
    if (k < nprlev) then
!
! Horizontal advection: u*z, v*z, energy conversion term (omega/p),
! vertical advection for interface above.  Pure pressure region first.
!
      do i=1,nlon
         dpslat(i,k) = 0._r8
         dpslon(i,k) = 0._r8
         tmpk   = 0.5_r8*rpdel(i,k)*edotdpde(i,k  )
         tmpkp1 = 0.5_r8*rpdel(i,k)*edotdpde(i,k+1)
         fu(i,k) = fu(i,k) + vm1(i,k)*z(i,k) - udel(i,k-1)*tmpk - udel(i,k  )*tmpkp1
         fv(i,k) = fv(i,k) - um1(i,k)*z(i,k) - vdel(i,k-1)*tmpk - vdel(i,k  )*tmpkp1
         vat  (i,k) = - (tdel(i,k-1)*tmpk + tdel(i,k)*tmpkp1)
         ktoop(i,k) = cappa*tv(i,k)/(1._r8 + cpvir*qm1(i,k))* &
                      omga(i,k)*rpmid(i,k)
         t2   (i,k) = t2(i,k) + d(i,k)*tm1(i,k) - tdel(i,k-1)*tmpk + &
                      ktoop(i,k) - tdel(i,k)*tmpkp1
      end do
!
    else if (k < plev) then
!
! Hybrid region above bottom level: Computations are the same as in pure
! pressure region, except that pressure gradient terms are added to 
! momentum tendencies.
!
      do i=1,nlon
         tmpk   = 0.5_r8*rpdel(i,k)*edotdpde(i,k  )
         tmpkp1 = 0.5_r8*rpdel(i,k)*edotdpde(i,k+1)
         tmp = rtv(i,k)*hybm(k)*rpmid(i,k)*pbot(i)
         dpslon(i,k) = rcoslat*tmp*dpsl(i)
         dpslat(i,k) = rcoslat*tmp*dpsm(i)
         fu(i,k) = fu(i,k) + vm1(i,k)*z(i,k) - udel(i,k-1)*tmpk - &
            udel(i,k  )*tmpkp1 - dpslon(i,k)
         fv(i,k) = fv(i,k) - um1(i,k)*z(i,k) - vdel(i,k-1)*tmpk - &
            vdel(i,k  )*tmpkp1 - dpslat(i,k)
         vat  (i,k) = - (tdel(i,k-1)*tmpk + tdel(i,k)*tmpkp1)
         ktoop(i,k) = cappa*tv(i,k)/(1._r8 + cpvir*qm1(i,k))* &
                      omga(i,k)*rpmid(i,k)
         t2   (i,k) = t2(i,k) + d(i,k)*tm1(i,k) - tdel(i,k-1)*tmpk + &
                      ktoop(i,k) - tdel(i,k)*tmpkp1
      end do
!
    else
!
! Bottom level
!
      do i=1,nlon
         tmpk = 0.5_r8*rpdel(i,plev)*edotdpde(i,plev  )
         tmp  = rtv(i,plev)*hybm(plev)*rpmid(i,plev)*pbot(i)
         dpslon(i,plev) = rcoslat*tmp*dpsl(i)
         dpslat(i,plev) = rcoslat*tmp*dpsm(i)
         fu(i,plev) = fu(i,plev) + vm1(i,plev)*z(i,plev) - &
            udel(i,plev-1)*tmpk - dpslon(i,plev)
         fv(i,plev) = fv(i,plev) - um1(i,plev)*z(i,plev) - &
            vdel(i,plev-1)*tmpk - dpslat(i,plev)
         vat  (i,plev) = -(tdel(i,plev-1)*tmpk)
         ktoop(i,plev) = cappa*tv(i,plev)/(1._r8 + cpvir*qm1(i,plev))* &
                         omga(i,plev)*rpmid(i,plev)
         t2   (i,plev) = t2(i,plev) + d(i,plev)*tm1(i,plev) - &
                         tdel(i,plev-1)*tmpk + ktoop(i,plev)
      end do
!
    end if
!
   enddo
!
! Convert eta-dot(dp/deta) to eta-dot (top and bottom = 0.)
!
   etadot(:,1) = 0._r8
   etadot(:,plevp) = 0._r8
!$OMP PARALLEL DO PRIVATE (K, TMP, I)
   do k=2,plev
      tmp = etamid(k) - etamid(k-1)
      do i=1,nlon
         etadot(i,k) = edotdpde(i,k)*tmp/(pmid(i,k) - pmid(i,k-1))
      end do
   end do
!
!-----------------------------------------------------------------------
!
! Divergence and hydrostatic equations
!
! Del squared part of RHS of divergence equation.
! Kinetic energy and diagonal term of hydrostatic equation.
! Total temperature as opposed to  perturbation temperature is acceptable
! since del-square operator will operate on this term.
! (Also store some temporary terms.)
!
!$OMP PARALLEL DO PRIVATE (K, I)
   do k=1,plev
      do i=1,nlon
         tterm(i,k) = 0.5_r8*tm2(i,k) - tm1(i,k)
         pterm(i,k) = rtv(i,k)*rpmid(i,k)*pdel(i,k)
         drhs(i,k) = phis(i) + engy(i,k) + rtv(i,k)*0.5_r8* &
            rpmid(i,k)*pdel(i,k) + href(k,k)*tterm(i,k) + &
            bps(k)*(0.5_r8*logpsm2(i) - logpsm1(i))
      end do
   end do

!
! Bottom level term of hydrostatic equation
!
!$OMP PARALLEL DO PRIVATE (K, I)
   do k=1,plev-1
      do i=1,nlon
         drhs(i,k) = drhs(i,k) + rtv(i,plev)* &
            rpmid(i,plev)*pdel(i,plev) + &
            href(plev,k)*tterm(i,plev)
      end do
   end do
!
! Interior terms of hydrostatic equation
!
!$OMP PARALLEL DO PRIVATE (K, KK, I)
   do k=1,plev-2
      do kk=k+1,plev-1
         do i=1,nlon
            drhs(i,k) = drhs(i,k) + pterm(i,kk) + href(kk,k)*tterm(i,kk)
         end do
      end do
   end do
!    
   return
end subroutine grmult
